// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"
#include <iostream>

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    
    while (value == 0) { 			// semaphore not available
	queue->Append((void *)currentThread);	// so go to sleep
	currentThread->Sleep();
    } 
    value--; 					// semaphore available, 
						// consume its value
    
    (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
	scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!


Lock::Lock(char* debugName) 
{
	#ifdef CHANGED
	name=debugName;
	owner=NULL;
	lockState=FREE;
	waitQueue=new List;
	#endif
}
Lock::~Lock() 
{
#ifdef CHANGED
	delete waitQueue;
#endif
}
void Lock::Acquire() 
{
	//printf("In Acquire!!!\n");
#ifdef CHANGED
	//disable interrupts
	IntStatus oldInterruptState = interrupt->SetLevel(IntOff);
	//printf("disable interrupts\n");

	if(isHeldByCurrentThread()) //if current Thread in CPU owns lock
	{
		//do nothing
		//restore the interrupt
		//printf("already owned, do nothing, restore the interrupt\n");
		(void) interrupt->SetLevel(oldInterruptState);
		//exit the void function
		return;
	}
	
	if (lockState) //if lockState is FREE or true
	{
		//printf("make the lock Busy, so nobody can use it\n");
		//make the lock Busy, so nobody can use it
		lockState=BUSY;
		//make currentThread to be the lock owner
		owner=currentThread;
	}
	else //if lockState is BUSY or false
	{
		//add currentThread to the waiting queue
		//printf("add currentThread to the waiting queue\n");
		waitQueue->Append((void*)currentThread);
		//currentThread now goes to sleep
		currentThread->Sleep();
	}

	//restore interrupts
	(void) interrupt->SetLevel(oldInterruptState);
	
	#endif
}
void Lock::Release() 
{
	#ifdef CHANGED
	//disable interrupts
	IntStatus oldInterruptState = interrupt->SetLevel(IntOff);

	if(!isHeldByCurrentThread()) //if current Thread in CPU does NOT own the lock
	{
		//problem; non-current threads should not be able to release locks
		//print error message
		//print out error message from debug mode only
		DEBUG('t', "Not lock owner has no right to release the lock\n");
		//restore the interrupt
		(void) interrupt->SetLevel(oldInterruptState);
		//exit the void function
		return;
	}
	if (!waitQueue->IsEmpty()) // waitQueue is NOT empty
	{
		//Removes a thread from waiting queue
		Thread* newReadyThread = (Thread*)waitQueue->Remove();
		//ThreadStatus status = READY;
		//and adds it to ready queue
		scheduler->ReadyToRun(newReadyThread);
		//and changes it status
		//newReadyThread->setStatus(status);//Sets thread status to READY
		//new thread becomes thread owner
		owner=newReadyThread;
	}
	else //wait queue is empty
	{ 
		//make Lock available and reset Owner
		lockState=FREE;
		owner=NULL;
	}
	//restore interrupts
	(void) interrupt->SetLevel(oldInterruptState);
	#endif
}

bool Lock::isHeldByCurrentThread() 
{ 
	#ifdef CHANGED
	return owner==currentThread;
	#endif
}

Condition::Condition(char* debugName) 
{
	#ifdef CHANGED
	name=debugName;
	firstLock=NULL;
	waitQueue= new List;
	#endif
}
Condition::~Condition() 
{
#ifdef CHANGED
	delete waitQueue;
#endif
}
void Condition::Wait(Lock* conditionLock)
{
	#ifdef CHANGED
	//disable interrupts
	IntStatus oldInterruptState = interrupt->SetLevel(IntOff);

	if(conditionLock==NULL)
	{
		DEBUG('t', "Null lock calling wait.\n");
		//restore interrupts
		(void) interrupt->SetLevel(oldInterruptState);
		return;
	}
	if(waitQueue->IsEmpty())
		firstLock=conditionLock;
	
	conditionLock->Release();
	waitQueue->Append((void*)currentThread);
	currentThread->Sleep();
	conditionLock->Acquire();
	//restore interrupts
	(void) interrupt->SetLevel(oldInterruptState);
	#endif
}
void Condition::Signal(Lock* conditionLock) 
{ 
	#ifdef CHANGED
	//disable interrupts
	IntStatus oldInterruptState = interrupt->SetLevel(IntOff);

	if(waitQueue->IsEmpty())
	{
		//restore interrupts
		(void) interrupt->SetLevel(oldInterruptState);
		return;
	}
	if (firstLock!=conditionLock)
	{
		DEBUG('t', "Signaler's lock doesn't match.\n");
		//restore interrupts
		(void) interrupt->SetLevel(oldInterruptState);
		return;
	}
	//Removes a thread from waiting queue
	Thread* newReadyThread = (Thread*)waitQueue->Remove();
	//ThreadStatus status = READY;
	//and adds it to ready queue
	scheduler->ReadyToRun(newReadyThread);
	//and changes it status
	//newReadyThread->setStatus(status);//Sets thread status to READY
	if (waitQueue->IsEmpty())
		firstLock=NULL;

	//restore interrupts
	(void) interrupt->SetLevel(oldInterruptState);
	#endif
}
void Condition::Broadcast(Lock* conditionLock) 
{ 
	#ifdef CHANGED
	//Signal lock while there are threads waiting in the condition variable wait queue
	while(!waitQueue->IsEmpty())
		Signal(conditionLock);
	#endif
}

